package com.example.JavaWebApi.core.filehelper;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.aliyun.oss.OSS;
import com.aliyun.oss.OSSClientBuilder;
import com.aliyun.oss.model.OSSObject;
import com.aliyuncs.DefaultAcsClient;
import com.aliyuncs.IAcsClient;
import com.aliyuncs.mts.model.v20140618.SubmitJobsRequest;
import com.aliyuncs.mts.model.v20140618.SubmitJobsResponse;
import com.aliyuncs.profile.DefaultProfile;
import com.example.JavaWebApi.core.commonmodel.Exceptions.BusinessException;
import com.example.JavaWebApi.core.commonmodel.ResponseTypeEnum;
import com.example.JavaWebApi.model.configmodel.MinioConfig;
import com.example.JavaWebApi.model.configmodel.OssConfig;
import com.example.JavaWebApi.model.configmodel.TranCodeConfig;
import com.google.common.collect.HashMultimap;
import com.google.common.collect.Multimap;
import io.minio.*;
import io.minio.errors.*;
import io.minio.http.Method;
import io.minio.messages.Part;
import lombok.Data;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.tomcat.util.http.fileupload.IOUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;
import sun.misc.BASE64Decoder;

import javax.validation.constraints.NotNull;
import java.io.*;
import java.net.URLEncoder;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
import java.util.zip.Adler32;
import java.util.zip.CheckedOutputStream;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

@Slf4j
@Component
public class uploadFile {
    @Value("${spring.web.uploadpath}")
    private String targetFilePath;
    @Autowired
    private OssConfig ossConfig;
    @Autowired
    private TranCodeConfig tranCodeConfig;
    @Autowired
    private MinioConfig minioConfig;
    /**
     * 上传附件示例
     * @return
     */
    @SneakyThrows
    public String uploadFile(MultipartFile file) {
        //String targetFilePath = "";//路径
        File folder=new File(targetFilePath);
        //上传文件夹不存在就创建一个
        if (!folder.exists()){
            folder.mkdir();
        }
        String[] fileInfo=file.getOriginalFilename().split("\\.");
        String fileExtendName=fileInfo[fileInfo.length-1];
        String fileName = UUID.randomUUID().toString().replace("-", "");
        File targetFile = new File(targetFilePath + File.separator + fileName+"."+ fileExtendName);

        FileOutputStream fileOutputStream = null;
        fileOutputStream = new FileOutputStream(targetFile);
        IOUtils.copy(file.getInputStream(), fileOutputStream);
        return fileName+"."+fileExtendName;
    }

    /**
     * Description: 阿里云批量下载并打包
     *
     * @param ossPaths 路径集合
     * @param filePath zip路径
     * @param zipName  zip的名字
     * @return void
     * @author 杨泽
     * @date 2023/8/18 15:13
     */
    public void saveForZip(List<String> ossPaths, String filePath, String zipName) {
        File zipFile = null;
        try {
            File folder = new File(filePath);
            //上传文件夹不存在就创建一个
            if (!folder.exists()) {
                folder.mkdir();
            }
            zipFile = new File(filePath + File.separator + zipName + ".zip");
            FileOutputStream f = new FileOutputStream(zipFile);
            CheckedOutputStream csum = new CheckedOutputStream(f, new Adler32());
            OSS client = createOss();
            // 用于将数据压缩成Zip文件格式
            ZipOutputStream zos = new ZipOutputStream(csum);
            for (String ossPath : ossPaths) {
                InputStream inputStream = null;
                log.info(ossPath + "正在下载！");
                if (ossPath == null || !StringUtils.isNotBlank(ossPath)) {
                    continue;
                }
                if (ossPath.startsWith("/")) {
                    ossPath = ossPath.substring(1);
                }
                try {
                    OSSObject ossObject = client.getObject(ossConfig.getAliyunOssBucket(), ossPath);
                    // 读去Object内容 返回
                    inputStream = ossObject.getObjectContent();
                    //String realName=ossPath.substring(ossPath.lastIndexOf("/")+1);
                    String name = UUID.randomUUID().toString().replace("-", "") + ossPath.substring(ossPath.lastIndexOf("."));
                    zos.putNextEntry(new ZipEntry(name));
                    int bytesRead = 0;
                    while ((bytesRead = inputStream.read()) != -1) {
                        zos.write(bytesRead);
                    }
                    log.info(ossPath + "下载完毕！");

                } catch (Exception e) {
                    log.error(e.getMessage());
                    continue;
                } finally {
                    inputStream.close();
                    zos.closeEntry();
                }

            }
            zos.close();

        } catch (IOException e) {
            log.error(e.getMessage());
            throw new Error(e.getMessage());
        }
    }

//    @SneakyThrows
//    public Long timeSum(String url)  {
//        log.info("视频url地址 ：{}", url);
//        try{
//            MultimediaObject ffmpegFileInfo = new MultimediaObject(new URL(url));
//            ffmpegFileInfo.setFile(new File(url));
//            ws.schild.jave.info.MultimediaInfo multimediaInfo = null;
//            multimediaInfo = ffmpegFileInfo.getInfo();
//            long playTime = multimediaInfo.getDuration();
//            log.info("视频时长 ：{}",playTime);
//            return playTime;
//        }
//        catch (Exception e){
//            log.error("解析失败！！"+ JSON.toJSONString(e));
//            throw e;
//        }
//    }

    /**
     * 获取音频或视频信息
     *
     * @param videoAudioResourcesPath
     */
    public JSONObject videoAudioInfo(String videoAudioResourcesPath) throws IOException {
        String picCommend = "ffprobe -show_format " + videoAudioResourcesPath;
        JSONObject jsonObject = runCmd(picCommend);
        return jsonObject;
    }

    /**
     * 调用命令行执行，并返回信息
     *
     * @param command 命令行参数
     */
    public JSONObject getInfoStr(List command) {
        JSONObject jsonObject = new JSONObject();
        ProcessBuilder builder = new ProcessBuilder();
        //正常信息和错误信息合并输出
        builder.redirectErrorStream(true);
        builder.command(command);
        //开始执行命令
        Process process = null;
        try {
            process = builder.start();
            //如果你想获取到执行完后的信息，那么下面的代码也是需要的
            String line = "";
            BufferedReader br = new BufferedReader(new InputStreamReader(process.getInputStream()));
            while ((line = br.readLine()) != null) {
                if (line.trim().startsWith("duration=")) {
                    //根据字符匹配进行切割
                    jsonObject.put("duration", line.trim().substring(line.trim().indexOf("=") + 1, line.trim().length()));
                }
                if (line.trim().startsWith("size=")) {
                    //根据字符匹配进行切割
                    jsonObject.put("size", line.trim().substring(line.trim().indexOf("=") + 1, line.trim().length()));
                }
                if (line.trim().startsWith("format_long_name=")) {
                    //根据字符匹配进行切割
                    jsonObject.put("format", line.trim().substring(line.trim().indexOf("=") + 1, line.trim().length()));
                }
                if (line.contains("fps")) {
                    String[] a = line.split(",");
                    for (int i = 1; i < a.length; i++) {
                        if (a[i].contains("x")) {
                            String definition = a[i].trim().split(" ")[0];
                            jsonObject.put("resolution", definition);
                        }
                    }
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        return jsonObject;
    }

    public JSONObject runCmd(String command) throws IOException {
        JSONObject jsonObject = new JSONObject();
        Runtime rt = Runtime.getRuntime();
        Process proc = rt.exec(command);
        InputStream stderr = proc.getErrorStream();
        InputStreamReader isr = new InputStreamReader(stderr);
        BufferedReader br = new BufferedReader(isr);
        String line = null;
        while ((line = br.readLine()) != null) {
            System.out.println(line);
            if (line.contains("Duration:")) {
                String[] a = line.split(",");
                for (int i = 0; i < a.length; i++) {
                    if (a[i].contains("Duration")) {
                        String definition = a[i].trim().split(" ")[1];

                        String[] my = definition.split(":");

                        int hour = Integer.parseInt(my[0]);

                        int min = Integer.parseInt(my[1]);

                        double sec = Double.parseDouble(my[2]);

                        double zong = hour * 3600 + min * 60 + sec;
                        jsonObject.put("duration", zong);
                    }
                    if (a[i].contains("bitrate:")) {
                        String definition = a[i].trim().split(" ")[1];
                        int hour = Integer.parseInt(definition);
                        jsonObject.put("BitRate", hour);
                    }
                }
            }
//            if(line.contains("Input")){
//                String definition = line.split(",")[1];
//                jsonObject.put("format",definition);
//            }
            if (line.contains("Video:")) {
                String[] a = line.split(",");
                for (int i = 0; i < a.length; i++) {
                    if (a[i].contains("Video:")) {
                        String definition = a[i].trim().split(" ")[3];
                        //根据字符匹配进行切割
                        jsonObject.put("VideoEncode", definition);
                    }
                }

            }
            if (line.contains("fps")) {
                String[] a = line.split(",");
                for (int i = 1; i < a.length; i++) {
                    if (a[i].contains("x")) {
                        String definition = a[i].trim().split(" ")[0];
                        jsonObject.put("resolution", definition);
                        if (a[i].split(" ").length > 4) {
                            String DisplayScale = a[i].trim().split(" ")[4];
                            jsonObject.put("DisplayScale", DisplayScale.replace("]", ""));
                        } else {
                            jsonObject.put("DisplayScale", "16:9");
                        }
                        System.out.println(line);
                    }
                    if (a[i].contains("fps")) {
                        String FrameRate = a[i].trim().split(" ")[0];
                        jsonObject.put("FrameRate", FrameRate);
                    }
                }
            }
        }
        return jsonObject;
    }

    /**
     * Description: 阿里云转码
     *
     * @param filePath 文件路径
     * @return java.lang.String
     * @author 杨泽
     * @date 2023/9/1 14:09
     */
    @SneakyThrows
    public String tranCode(String filePath) {
        try {
            String[] arr = filePath.split("\\/");
            String fileName = arr[arr.length - 1];
            IAcsClient client = createAcsClient();
            SubmitJobsRequest request = new SubmitJobsRequest();
            JSONObject input = new JSONObject();
            input.put("Location", ossConfig.getAliyunOssLocation());
            input.put("Bucket", ossConfig.getAliyunOssBucket());
            input.put("Object", URLEncoder.encode(filePath, "utf-8"));
            request.setInput(input.toJSONString());
            JSONArray outputs = new JSONArray();
            JSONObject output = new JSONObject();
            output.put("OutputObject", tranCodeConfig.getSavePathPrefix() + fileName);
            output.put("TemplateId", tranCodeConfig.getTemplateId());
            outputs.add(output);
            request.setOutputs(outputs.toJSONString());
            request.setOutputBucket(ossConfig.getAliyunOssBucket());
            request.setOutputLocation(ossConfig.getAliyunOssLocation());
            request.setPipelineId(tranCodeConfig.getPipelineId());
            SubmitJobsResponse submitJobsResponse = client.getAcsResponse(request);
            SubmitJobsResponse.JobResult jobResult = submitJobsResponse.getJobResultList().get(0);
            if (jobResult.getSuccess()) {
                return tranCodeConfig.getSavePathPrefix() + fileName;
            } else {
                return "";
            }
        } catch (Exception e) {
            log.error(e.getMessage());
            throw e;
        }

    }

    public String minioUpload(Base64UploadInfo base64UploadInfo) {
        InputStream byteArrayInputStream = null;
        try {
            byteArrayInputStream = base64ConvertPNG(base64UploadInfo.getRealImage());
        } catch (Exception e) {
            log.error(base64UploadInfo.getObjectName() + "图片base64转换失败！");
            return "";
        }
        MinioClient minioClient = createMinioClient();
        boolean exists = false;
        try {
            exists = minioClient.bucketExists(BucketExistsArgs.builder().bucket(minioConfig.getDefaultBucket()).build());
            if (!exists) {
                minioClient.makeBucket(MakeBucketArgs.builder().bucket(minioConfig.getDefaultBucket()).build());
            }
            byteArrayInputStream = base64ConvertPNG(base64UploadInfo.getRealImage());
            minioClient.putObject(PutObjectArgs.builder()
                    .bucket(minioConfig.getDefaultBucket())
                    .object(base64UploadInfo.getObjectName())
                    .stream(byteArrayInputStream, byteArrayInputStream.available(), -1)
                    .contentType(base64UploadInfo.getContentType())
                    .build());
            Map<String, String> reqParams = new HashMap<>();
            reqParams.put("response-content-type", "application/json");
            String url = getFileUrl(base64UploadInfo.getObjectName());
            return url;
        } catch (Exception e) {
            log.error("直接上传文件失败！message" + e.getMessage());
            throw new BusinessException(ResponseTypeEnum.Exception, e.getMessage());
        }
    }

    public String minioUpload(FileUploadInfo fileUploadInfo) {
        MinioClient minioClient = createMinioClient();
        boolean exists = false;
        try {
            exists = minioClient.bucketExists(BucketExistsArgs.builder().bucket(minioConfig.getDefaultBucket()).build());
            if (!exists) {
                minioClient.makeBucket(MakeBucketArgs.builder().bucket(minioConfig.getDefaultBucket()).build());
            }

            minioClient.putObject(PutObjectArgs.builder()
                    .bucket(minioConfig.getDefaultBucket())
                    .object(fileUploadInfo.getObjectName())
                    .stream(fileUploadInfo.getFile().getInputStream(), fileUploadInfo.getFile().getSize(), -1)
                    .contentType(fileUploadInfo.getFile().getContentType())
                    .build());
            Map<String, String> reqParams = new HashMap<>();
            reqParams.put("response-content-type", "application/json");
            String url = getFileUrl(fileUploadInfo.getObjectName());
            return url;
        } catch (Exception e) {
            log.error("直接上传文件失败！message" + e.getMessage());
            throw new BusinessException(ResponseTypeEnum.Exception, e.getMessage());
        }

    }

    /**
     * Description: minio获取分片上传地址
     *
     * @param partParameter
     * @return com.example.JavaWebApi.core.filehelper.FileHelper.MinioUploadPartUrl
     * @author 杨泽
     * @date 2023/9/1 15:42
     */
    @SneakyThrows
    public MinioUploadPartUrl getMinioUploadPartUrl(PartParameter partParameter) {
        try {
            MinioUploadPartUrl minioPartUrl = new MinioUploadPartUrl();
            MinioClient client = createMinioClient();
            int page = (int) Math.ceil((double) partParameter.getFileSize() / partParameter.getPieceSize());
            HashMultimap<String, String> headers = HashMultimap.create();
            headers.put("Content-Type", partParameter.getContentType());
            MyMinio myMinio = new MyMinio(client);
            String uploadId = myMinio.initMultiPartUpload(minioConfig.getDefaultBucket(), null, partParameter.getFileName(), headers, null);
            minioPartUrl.setUploadId(uploadId);
            minioPartUrl.setFileName(partParameter.getFileName());
            Map<String, String> reqParams = new HashMap<>();
            reqParams.put("uploadId", uploadId);
            for (int i = 1; i <= page; i++) {
                reqParams.put("partNumber", String.valueOf(i));
                //返回带签名URL
                String uploadUrl = myMinio.getPresignedObjectUrl(
                        GetPresignedObjectUrlArgs.builder()
                                .method(Method.PUT)//GET方式请求
                                .bucket(minioConfig.getDefaultBucket())//存储桶的名字
                                .object(minioPartUrl.getFileName())//文件的名字
                                .expiry(1, TimeUnit.HOURS)//上传地址有效时长
                                .extraQueryParams(reqParams)//指定任务ID和当前是第几个分块，生成上传链接
                                .build());
                minioPartUrl.getUrls().add(uploadUrl);
            }
            return minioPartUrl;
        } catch (Exception e) {
            log.error("getMinioUploadPartUrl" + e.getMessage());
            throw new BusinessException(ResponseTypeEnum.Exception, "获取文件上传路径失败！");
        }

    }

    /**
     * Description: minio合并上传任务
     *
     * @return boolean
     * @author 杨泽
     * @date 2023/9/1 15:43
     */
    public boolean mergeUpload(FileMerge fileMerge) {
        try {
            MyMinio myMinio = new MyMinio(createMinioClient());
            Part[] parts = new Part[10000];
            ListPartsResponse partResult = myMinio.listMultipart(minioConfig.getDefaultBucket(), null, fileMerge.getFileName(), 1000, 0, fileMerge.getUploadId(), null, null);
            int partNumber = 1;
            for (Part part : partResult.result().partList()) {
                parts[partNumber - 1] = new Part(partNumber, part.etag());
                partNumber++;
            }
            myMinio.mergeMultipartUpload(minioConfig.getDefaultBucket(), null, fileMerge.getFileName(), fileMerge.getUploadId(), parts, null, null);
        } catch (Exception e) {
            log.error("mergeUpload" + e.getMessage());
            throw new BusinessException(ResponseTypeEnum.Exception, e.getMessage());
        }
        return true;
    }

    /**
     * Description: 获取文件访问路径
     *
     * @param fileName
     * @return java.lang.String
     * @author 杨泽
     * @date 2023/9/1 16:12
     */
    public String getFileUrl(String fileName) {
        try {
            if (minioConfig.getValidateUrl()) {
                return createMinioClient().
                        getPresignedObjectUrl(GetPresignedObjectUrlArgs.builder()
                                .expiry(minioConfig.getExpiry(), TimeUnit.HOURS)
                                .bucket(minioConfig.getDefaultBucket())
                                .object(fileName)
                                .method(Method.GET)
                                .build());
            } else {
                return minioConfig.getDefaultPreviewUrl(fileName);
            }
        } catch (Exception e) {
            log.error("getFileUrl" + e.getMessage());
            throw new BusinessException(ResponseTypeEnum.Exception, "文件不存在！");
        }
    }

    public byte[] download(String fileName) {
        try {
            ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
            InputStream stream = null;
            stream = createMinioClient().getObject(GetObjectArgs
                    .builder()
                    .bucket(minioConfig.getDefaultBucket())
                    .object(fileName)
                    .build());
            return fileToByte(stream);
        } catch (Exception e) {
            log.error("下载文件错误！" + e.getMessage());
            throw new BusinessException(ResponseTypeEnum.Exception, "下载文件错误！");
        }
    }

    /**
     * Description: 批量获取文件路径
     *
     * @param fileNames
     * @return java.util.HashMap<java.lang.String, java.lang.String>
     * @author 杨泽
     * @date 2023/9/4 13:50
     */
    public HashMap<String, String> getFileUrls(Collection<String> fileNames) {
        HashMap<String, String> hashMap = new HashMap<>();
        MinioClient client = createMinioClient();
        for (String fileName : fileNames.stream().distinct().collect(Collectors.toList())) {
            if (minioConfig.getValidateUrl()) {
                try {
                    String url = client.getPresignedObjectUrl(GetPresignedObjectUrlArgs.builder()
                            .expiry(minioConfig.getExpiry(), TimeUnit.HOURS)
                            .bucket(minioConfig.getDefaultBucket())
                            .object(fileName)
                            .method(Method.GET)
                            .build());
                    hashMap.put(fileName, url);
                } catch (Exception e) {
                    log.error("获取文件路径失败" + e.getMessage());
                }
            } else {
                hashMap.put(fileName, minioConfig.getDefaultPreviewUrl(fileName));
            }
        }
        return hashMap;

    }

    public static byte[] fileToByte(InputStream fileInputStream) {
        ByteArrayOutputStream byteArrayOutputStream = null;
        try {
            byteArrayOutputStream = new ByteArrayOutputStream();
            byte[] b = new byte[1024];
            int n;
            while ((n = fileInputStream.read(b)) != -1) {
                byteArrayOutputStream.write(b, 0, n);
            }
            return byteArrayOutputStream.toByteArray();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (byteArrayOutputStream != null) {
                try {
                    byteArrayOutputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (fileInputStream != null) {
                try {
                    fileInputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        return null;
    }

    public InputStream base64ConvertPNG(String base64) {
        BASE64Decoder decoder = new BASE64Decoder();
        //解码
        base64 = base64.replaceAll(" ", "+");
        try {
            byte[] buffer = decoder.decodeBuffer(base64);
            for (int i = 0; i < buffer.length; i++) {
                if (buffer[i] < 0) {
                    buffer[i] += 256;
                }
            }
            //生成流
            ByteArrayInputStream stream = new ByteArrayInputStream(buffer);
            return stream;
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    private MinioClient createMinioClient() {
        return MinioClient.builder()
                .endpoint(minioConfig.getEndpoint())
                .credentials(minioConfig.getAccessKey(), minioConfig.getSecretKey())
                .build();
    }

    private OSS createOss() {
        return new OSSClientBuilder().build(ossConfig.getAliyunOssEndponit(), ossConfig.getAliyunOssKeyId(), ossConfig.getAliyunOssKeySecret());
    }

    private IAcsClient createAcsClient() {
        DefaultProfile profile = DefaultProfile.getProfile("cn-beijing", ossConfig.getAliyunOssKeyId(), ossConfig.getAliyunOssKeySecret());
        return new DefaultAcsClient(profile);
    }

    @Data
    public static class Base64UploadInfo {
        @NotNull(message = "objectName不能为空！")
        private String objectName;
        @NotNull(message = "image不能为空！")
        private String image;
        private String contentType = "application/octet-stream";

        public String getRealImage() {
            String[] arr = image.split(",");
            if (arr.length < 2) {
                return "";
            }
            return arr[1];
        }
    }

    @Data
    public static class FileUploadInfo {
        private MultipartFile file;
        private String objectName;
    }

    @Data
    public static class PartParameter {
        @NotNull(message = "文件大小不能为空！")
        private Long fileSize;
        @NotNull(message = "分片大小不能为空！")
        private Long pieceSize;
        @NotNull(message = "hashcode不能为空！")
        private String hashCode;
        @NotNull(message = "fileName不能为空！")
        private String fileName;
        private String contentType = "application/octet-stream";
    }

    @Data
    public static class MinioUploadPartUrl {
        private String uploadId;
        private String fileName;
        private List<String> urls = new ArrayList<>();
    }

    @Data
    public static class FileMerge {
        @NotNull(message = "uploadId不能为空")
        private String uploadId;
        @NotNull(message = "fileName不能为空")
        private String fileName;
    }

    public static class MyMinio extends MinioClient {
        protected MyMinio(MinioClient client) {
            super(client);
        }

        public String initMultiPartUpload(String bucket, String region, String object, Multimap<String, String> headers, Multimap<String, String> extraQueryParams) throws IOException, InvalidKeyException, NoSuchAlgorithmException, InsufficientDataException, ServerException, InternalException, XmlParserException, InvalidResponseException, ErrorResponseException, ServerException, InsufficientDataException, ErrorResponseException, NoSuchAlgorithmException, IOException, InvalidKeyException, XmlParserException, InvalidResponseException, ServerException, InsufficientDataException, ErrorResponseException, XmlParserException, InvalidResponseException {
            CreateMultipartUploadResponse response = this.createMultipartUpload(bucket, region, object, headers, extraQueryParams);

            return response.result().uploadId();
        }

        //合并指定上传任务的分块文件
        public ObjectWriteResponse mergeMultipartUpload(String bucketName, String region, String objectName, String uploadId, Part[] parts, Multimap<String, String> extraHeaders, Multimap<String, String> extraQueryParams) throws IOException, InvalidKeyException, NoSuchAlgorithmException, InsufficientDataException, ServerException, InternalException, XmlParserException, InvalidResponseException, ErrorResponseException {

            return this.completeMultipartUpload(bucketName, region, objectName, uploadId, parts, extraHeaders, extraQueryParams);
        }

        //获取指定上传任务内的已上传的分块信息
        public ListPartsResponse listMultipart(String bucketName, String region, String objectName, Integer maxParts, Integer partNumberMarker, String uploadId, Multimap<String, String> extraHeaders, Multimap<String, String> extraQueryParams) throws NoSuchAlgorithmException, InsufficientDataException, IOException, InvalidKeyException, ServerException, XmlParserException, ErrorResponseException, InternalException, InvalidResponseException {
            return this.listParts(bucketName, region, objectName, maxParts, partNumberMarker, uploadId, extraHeaders, extraQueryParams);
        }
    }
}
